use std::cmp::{max, min};
use cli_table::{print_stdout, Table, WithTitle};
use tracing::warn;
use crate::statm::MemInfo;

// const MAX_RECORDS: usize = 2048;
const CAPACITY: usize = 4096;

#[derive(Debug)]
pub struct Records {
    pub data: Vec<u64>,
    pub avg: f64,
    pub max: u64,
    pub min: u64,
}

impl Records {
    pub fn new() -> Self {
        Records {
            data: Vec::with_capacity(CAPACITY),
            avg: 0.0,
            max: 0,
            min: 0,
        }
    }

    pub fn add(&mut self, value: u64) {
        self.data.push(value);
        self.update();
        // self.slim();
    }

    pub fn update(&mut self) {
        if let Some(last_num) = self.data.last() {
            if self.data.len() == 1 {
                self.avg = *last_num as f64;
                self.max = *last_num;
                self.min = *last_num;
            } else {
                self.max = max(self.max, *last_num);
                self.min = min(self.min, *last_num);
                self.avg = {
                    let old_sum = self.avg * (self.data.len() as f64 - 1.0);
                    let new_sum = old_sum + *last_num as f64;
                    new_sum / (self.data.len() as f64)
                };
            }
        }
    }



    // fn slim(&mut self) {
    //     let data = &mut self.data;
    //     let len = data.len();
    //     if len >= CAPACITY {
    //         for i in 0 .. MAX_RECORDS {
    //             data[i] = data[len - MAX_RECORDS + i];
    //         }
    //         data.truncate(MAX_RECORDS);
    //     }
    // }
}

#[derive(Debug)]
pub struct Statistics {
    pub size: Records,
    pub resident: Records,
    pub shared: Records,
    pub trs: Records,
    pub lrs: Records,
    pub drs: Records,
    pub dt: Records,
}

impl Statistics {
    pub fn new() -> Self {
        Statistics {
            size: Records::new(),
            resident: Records::new(),
            shared: Records::new(),
            trs: Records::new(),
            lrs: Records::new(),
            drs: Records::new(),
            dt: Records::new(),
        }
    }

    #[inline]
    #[allow(dead_code)]
    pub fn is_empty(&self) -> bool {
        self.size.data.is_empty()
    }

    #[inline]
    #[allow(dead_code)]
    pub fn len(&self) -> usize {
        self.size.data.len()
    }

    pub fn add(&mut self, info: &MemInfo) {
        self.size.add(info.size);
        self.resident.add(info.resident);
        self.shared.add(info.shared);
        self.trs.add(info.trs);
        self.lrs.add(info.lrs);
        self.drs.add(info.drs);
        self.dt.add(info.dt);
    }

    pub fn display(&self) {
        if self.is_empty() {
            warn!("Statistics is empty");
            return;
        }

        #[derive(Table)]
        struct Row {
            #[table(title="")]
            rawname: &'static str,
            size: u64,
            resident: u64,
            shared: u64,
            trs: u64,
            lrs: u64,
            drs: u64,
            dt: u64,
        }

        let current = Row {
            rawname: "current",
            size: *self.size.data.last().unwrap(),
            resident: *self.resident.data.last().unwrap(),
            shared: *self.shared.data.last().unwrap(),
            trs: *self.trs.data.last().unwrap(),
            lrs: *self.lrs.data.last().unwrap(),
            drs: *self.drs.data.last().unwrap(),
            dt: *self.dt.data.last().unwrap(),
        };

        let avg = Row {
            rawname: "avg",
            size: self.size.avg as u64,
            resident: self.resident.avg as u64,
            shared: self.shared.avg as u64,
            trs: self.trs.avg as u64,
            lrs: self.lrs.avg as u64,
            drs: self.drs.avg as u64,
            dt: self.dt.avg as u64,
        };

        let max = Row {
            rawname: "max",
            size: self.size.max,
            resident: self.resident.max,
            shared: self.shared.max,
            trs: self.trs.max,
            lrs: self.lrs.max,
            drs: self.drs.max,
            dt: self.dt.max,
        };

        let min = Row {
            rawname: "min",
            size: self.size.min,
            resident: self.resident.min,
            shared: self.shared.min,
            trs: self.trs.min,
            lrs: self.lrs.min,
            drs: self.drs.min,
            dt: self.dt.min,
        };

        let table = vec![current, avg, max, min];
        print_stdout(table.with_title()).unwrap();


    }
}


mod test {
    use super::*;
    #[test]
    fn test_records() {
        let mut records = Records::new();
        records.add(1);
        records.add(2);
        records.add(3);
        records.add(4);
        records.add(5);
        assert_eq!(records.avg, 3.0);
        assert_eq!(records.max, 5);
        assert_eq!(records.min, 1);
    }
    #[test]
    fn test_statistics() {
        let mut statistics = Statistics::new();
        for _ in 0..10 {
            let meminfo = MemInfo::new(1, 4).unwrap();
            statistics.add(&meminfo);
        }
        statistics.display();
    }
}